using System.Collections;
using System.Collections.Generic;
using DL.Tools;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.UIElements;

namespace DL.UIBinding
{
	
    /// <summary>
    /// 无线滚动列表 ，组合Collection类使用
    /// </summary>
    public class InfiniteScrollView : UIBehaviour
    {
        private ScrollRect m_ScrollRect;
        public ScrollRect ScrollRect
        {
            get {
                if (m_ScrollRect == null) {
                    m_ScrollRect = GetComponent<ScrollRect>();
                }
                return m_ScrollRect;
            }
        }
        private Rect m_ScrollViewWorldRect;
        
        [SerializeField]
        private RectTransform m_Content;
        [SerializeField]
        private RectTransform m_ViewPortRT;
        
        [SerializeField,Header("实例化预制体")]
        private RectTransform m_ItemPrefab;

        [SerializeField] private bool m_IsHorizontal;
        
        [SerializeField,Header("实例化数量")]
        private int m_MinSpawnCount;

        [SerializeField] private RectOffset m_Padding;
        
        [SerializeField] private Vector2 m_Spacing;//cell间隔
        
        public int HorizontalCount = 1;
        public int VerticalCount = 1;
        
        //标准Item Rect
        private Rect m_ItemRect;
        private RectTransform m_TempRect;
        
        //收集器
        private IInfinityCollection<InfinityContext> m_Collection;

        private bool m_isInit;

        //初始化 
        public void Init(IInfinityCollection<InfinityContext> coll)
        {
	        if(m_isInit) return;
	        m_Collection = coll;

	        m_FreeItems = new();
	        m_AllItems = new();
	        
	        Transform go = new GameObject("Standard Item").transform;
	        go.SetParent(m_Content);
	        m_TempRect = go.gameObject.AddComponent<RectTransform>();
	        var rect = m_ItemPrefab.rect;
	        m_TempRect.sizeDelta = new Vector2(rect.width, rect.height);
	        // 设置m_TempRect的锚点为左下角（0,1），这意味着它将相对于其父对象的左下角定位
	        m_TempRect.anchorMin = new Vector2(0, 1);
	        m_TempRect.anchorMax = new Vector2(0, 1);
	        
	        m_ItemRect = m_ItemPrefab.GetComponent<RectTransform>().rect;
	        // 获取ScrollRect的RectTransform组件的世界矩形，并将其赋值给m_ScrollViewWorldRect变量
	        m_ScrollViewWorldRect = ScrollRect.GetComponent<RectTransform>().WorldRect();
	        
	        var tmpSpawnCount = 0;
	        if (m_IsHorizontal)
	        {
		        // 如果是水平移动，计算SpawnCount
		        tmpSpawnCount = HorizontalCount * (VerticalCount + 1);

		        // 启用ScrollRect的水平滚动，禁用垂直滚动
		        ScrollRect.vertical = false;
		        ScrollRect.horizontal = true;
	        }
	        else {
		        
		        // 如果是垂直移动，计算SpawnCount
		        tmpSpawnCount = (HorizontalCount + 1) * VerticalCount;
		        // 启用ScrollRect的垂直滚动，禁用水平滚动
		        ScrollRect.vertical = true;
		        ScrollRect.horizontal = false;
	        }

	        if (m_MinSpawnCount < tmpSpawnCount)
	        {
		        Debugger.EO_LogWarning("设置的cell数量不会铺满整个Content\n" + "最小铺满Content的cell数量：" + tmpSpawnCount);
	        }

	        //创建对应个数的Item
	        for (int i = 0; i < m_MinSpawnCount; i++)
	        {
		        // 如果没有空闲的InfinityItem对象，则实例化一个新的InfinityItem对象
		        var item = Instantiate(m_ItemPrefab, m_Content).GetComponent<InfinityItem>();
		        // 将新实例添加到所有Item的列表中
		        m_AllItems.Add(item);
		        item.name = m_ItemPrefab.name + "_" + i;
		        RecycleItem(item);
	        }
	        
	        UpdateContentSize();
	        ScrollRect.onValueChanged.AddListener(OnScrollRectValueChangedCallBack);
	        m_isInit = true;
        }

        //数据刷新
        public void ReUpdateData(int index = 0)
        {
	        // 调用UpdateContentSize方法来更新内容的大小
	        UpdateContentSize();
	        OnDataUpdate(index);
        }

        private void OnDataUpdate(int index = 0)
        {
	        // 遍历所有需要重新计算的数据项
	        for (int i = m_Collection.Count - 1; i >= index; i--)
	        {
		        // 获取当前索引的数据项
		        var data = m_Collection.GetItem(i);

		        //如果是脏的，不用判断了直接回收
		        if (data.Dirty)
		        {
			        data.RefreshChange(i, GetLocalPosByIndex(i));
			        RecycleItem(data.Item);
			        data.Item = null;
		        }

		        //计算可见性
		        m_TempRect.anchoredPosition = data.LocalPos;
		        var isVisible = m_TempRect.WorldRect().Overlaps(m_ScrollViewWorldRect);

		        if (!isVisible)
		        {
			        RecycleItem(data.Item);
			        data.Item = null;
		        }
		        else
		        {
			        // 获取一个可用的 InfinityItem 对象，并传入数据
			        InfinityItem item = data.Item ? data.Item : GetFreeItem(data);
			        item.name = m_ItemPrefab.name + "_" + data.Index;
			        // 设置该 InfinityItem 对象的本地位置
			        item.SelfRT.anchoredPosition = data.LocalPos;
			        data.Item = item;
		        }

		        //重置脏位
		        if (data.Dirty)
		        {
			        data.SetDirty(false);
		        }
	        }
        }

        private void OnScrollRectValueChangedCallBack(Vector2 arg0)
        {
	        OnDataUpdate();
        }

        private int m_TotalCol;
        private int m_TotalRow;
        /// <summary>
        /// 更新容器大小
        /// </summary>
        private void UpdateContentSize()
        {
            // 获取数据总数
            int totalCount = m_Collection.Count;
            // 判断布局方向，如果是水平方向
            if (m_IsHorizontal)
            {
                // 计算总列数，通过将总数除以每列的元素数量
                int totalCol = totalCount / HorizontalCount;

                // 如果总数不能被每列的元素数量整除，说明最后一列不满，需要增加一列
                if (totalCount % HorizontalCount != 0)
                {
                    totalCol++;
                }

                if (m_TotalCol == totalCol)
                {
	                return;
                }

                m_TotalCol = totalCol;
                // 计算内容的宽度，包括所有列的宽度以及列与列之间的间隔
                float width = totalCol * m_ItemRect.width; // 所有列的宽度总和
                width += (totalCol - 1) * m_Spacing.x; // 列与列之间的间隔总和
                width += m_Padding.left; // 左边边距
                width += m_Padding.right; // 右边边距

                // 更新内容的大小，只改变宽度，高度保持不变
                m_Content.sizeDelta = new Vector2(width, m_Content.sizeDelta.y);
            }
            else
            {
                // 如果是垂直方向
                // 计算总行数，通过将总数除以每行的元素数量
                int totalRow = totalCount / VerticalCount;

                // 如果总数不能被每行的元素数量整除，说明最后一行不满，需要增加一行
                if (totalCount % VerticalCount != 0)
                {
                    totalRow++;
                }
                
                if (m_TotalRow == totalRow)
                {
	                return;
                }

                m_TotalRow = totalRow;
                // 计算内容的高度，包括所有行的高度以及行与行之间的间隔
                float height = totalRow * m_ItemRect.height; // 所有行的高度总和
                height += (totalRow - 1) * m_Spacing.y; // 行与行之间的间隔总和
                height += m_Padding.top; // 顶部边距
                height += m_Padding.bottom; // 底部边距

                // 更新内容的大小，只改变高度，宽度保持不变
                m_Content.sizeDelta = new Vector2(m_Content.sizeDelta.x, height);
            }
        }

        #region Pool
        //简单对象池
        protected Queue<InfinityItem> m_FreeItems;
        private List<InfinityItem> m_AllItems;

        /// <summary>
        /// 获取一个空闲Item
        /// </summary>
        /// <returns></returns>
        private InfinityItem GetFreeItem(InfinityContext context)
        {
	        InfinityItem item;
	        // 检查是否有可用的（空闲的）InfinityItem对象在队列中
	        if (m_FreeItems.Count > 0) 
	        {
		        // 从队列中取出一个空闲的InfinityItem对象
		        item = m_FreeItems.Dequeue();
		        // 将该对象的gameObject激活
		        item.gameObject.SetActive(true);
	        }
	        else
	        {
		        // 如果没有空闲的InfinityItem对象，则实例化一个新的InfinityItem对象
		        item = Instantiate(m_ItemPrefab, m_Content).GetComponent<InfinityItem>();
		        // 将新实例添加到所有Item的列表中
		        m_AllItems.Add(item);
	        }
	        item.Show(context);
	        return item;
        }

        /// <summary>
        /// 回收Item
        /// </summary>
        /// <param name="item"></param>
        public void RecycleItem(InfinityItem item) 
        {
	        if(item == null) return;
	        item.Hide();
	        // 将该对象的gameObject设置为非激活状态
	        item.gameObject.SetActive(false);
	        // 将该对象添加到空闲队列中，以便后续可以重新使用
	        m_FreeItems.Enqueue(item);
        }
        

        #endregion

        public Vector2 GetLocalPosByIndex(int i) =>  InfiniteExtensions.GetLocalPositionByIndex(i, HorizontalCount,
	        VerticalCount, m_IsHorizontal, m_ItemRect, m_Spacing, m_Padding);
        
        
        public void JumpToIndex(int index,float delay = 0.1f)
        {
	        //对Index处理，范围围为0-m_collection.Count - 1
	        if (index < 0) index = 0;
	        else if (index >= m_Collection.Count)
	        {
		        index = m_Collection.Count - 1;
	        }

	        //获取坐标  
	        Vector2 stPos = m_Content.anchoredPosition;
	        Vector2 disPos;
	        var localPos = m_Collection.GetItem(index).LocalPos;//Item中心相对于Content左上的坐标
	        //如果是横向，则只移动x方向
	        if (m_IsHorizontal)
	        {
		        // var disX = Mathf.Abs((int)localPos.x);
		        var disX = - localPos.x;
		        //按照滑动方向去添加/删除半个Item的宽度
		        //从左上角开始向右为负值
		        disX = stPos.x > disX ? disX - m_ItemRect.width / 2 : disX + m_ItemRect.width / 2;
		        disPos = new Vector2(disX, stPos.y);
	        }
	        else
	        {
		        // var disY = Mathf.Abs((int)localPos.y);
		        var disY = localPos.y;
		        //按照滑动方向去添加/删除半个Item的高度
		        disY = stPos.y > disY ? disY + m_ItemRect.height / 2 : disY - m_ItemRect.height / 2;
		        disPos = new Vector2(stPos.x, -disY);
	        }

	        m_Coroutine = StartCoroutine(TweenMoveToPosition(m_Content, stPos, disPos, delay));
        }
        
        Coroutine m_Coroutine;
        private IEnumerator TweenMoveToPosition(RectTransform content, Vector2 startPos, Vector2 endPos, float delay)
        {
	        bool running = true;
	        float passedTime = 0f;
	        var wfeof = new WaitForEndOfFrame();
	        while (running)
	        {
		        yield return wfeof;
		        passedTime += Time.deltaTime;
		        Vector2 cur;
		        //如果时间到了
		        if (passedTime >= delay)
		        {
			        cur = endPos;
			        running = false;
			        if (m_Coroutine != null)
			        {
				        StopCoroutine(m_Coroutine);
				        m_Coroutine = null;
			        }
		        }
		        else
		        {
			        cur = Vector2.Lerp(startPos, endPos, passedTime / delay);
		        }
		        content.anchoredPosition = cur;
	        }
        }
    }
}